# encoding: UTF-8

"""
在本文件中引入所有希望在系统中使用的策略类

这个字典中保存了需要运行的策略的名称和策略类的映射关系，
用户的策略类写好后，先在该文件中引入，并设置好名称，然后
在../config/strategy_setting.json中写入具体每个策略对象的类和合约设置。
"""
import importlib
import json
import os

import yaml

from strategy.strategy_data import StraParam, StraSetting
from config.configpath import config_path, strategy_path,yml_file_path
from const_struct.db_const import DbType
# from strategy.ctaDemo import DoubleEmaDemo
# from strategy.trend_strategy import TrendStrategy
from utils.get_no_child import get_no_child

# STRATEGY_CLASS = {}
# # STRATEGY_CLASS['DoubleEmaDemo'] = DoubleEmaDemo
# STRATEGY_CLASS['StrategyTrend'] = TrendStrategy

strategy_config_json_file = os.path.join(config_path, 'strategy_conf.json')
strategy_config_yaml_file = os.path.join(yml_file_path, 'stra_cfg.yml')


def load_strategy_conf(source_type=DbType.DBTYPE_JSON, **kwargs):
    if source_type == DbType.DBTYPE_JSON:
        strategy_name = None
        strategy = None
        params_path = None
        if 'strategy_name' in kwargs.keys():
            strategy_name = kwargs['strategy_name']
        if 'strategy' in kwargs.keys():
            strategy = kwargs['strategy']

        if 'params_path' in kwargs.keys():
            params_path = kwargs['params_path']
            return load_strategy_conf_from_json(strategy_name=strategy_name, strategy=strategy,
                                                params_path=params_path)
        else:
            return load_strategy_conf_from_json(strategy_name=strategy_name, strategy=strategy)
    elif source_type == DbType.DBTYPE_YAML:
        pass


def load_strategy_conf_from_json(strategy_name=None, strategy=None, params_path=None):
    """
    从json配置文件中读取策略参数,strategy_name strategy均为NONE，返回所有参数
    :param strategy_name: 策略类名或实例名
    :param strategy: 策略实例
    :param params_path: 参数配置文件绝对路径，如果==None，config/strategy_conf.json
    :return: list(StrategyParam)
    """
    if not params_path:
        params_path = os.path.join(config_path, 'strategy_conf.json')
    if os.path.isdir(params_path):
        params_path = os.path.join(params_path, 'strategy_conf.json')
    params = {}
    strategy_class_name = ''
    need_all = False
    # print(strategy_name)
    if not strategy_name:
        if not strategy:
            need_all = True
        else:
            strategy_name = strategy.instance_name
            strategy_class_name = strategy.class_name
    with open(params_path, encoding='utf8') as f:
        all_params = json.load(f)
        if need_all:
            return all_params
        if strategy_name in all_params.keys():
            params = all_params[strategy_name]
            return params
        elif strategy_class_name in all_params.keys():
            params = all_params[strategy_class_name]
            if strategy_name in params.keys():
                params = params[strategy_name]
            return params
        else:
            for k, v in all_params.items():
                if k in strategy_name:
                    params = all_params[k]
                    break
            if strategy_name in params.keys():
                params = params[strategy_name]
            return params


def save_params(target_type=DbType.DBTYPE_JSON, **kwargs):
    if target_type == DbType.DBTYPE_JSON:
        params = kwargs['params']
        if 'params_path' in kwargs.keys():
            params_path = kwargs['params_path']
            save_strategy_params_to_json(params, params_path=params_path)
        else:
            save_strategy_params_to_json(params)


def save_strategy_params_to_json(params, params_path=None):
    """
        将策略参数写入json配置文件中
        :param params:
        :param params_path: 参数配置文件绝对路径，如果==None，config/strategy_conf.json
        :return: None
        """
    if not params_path:
        params_path = os.path.join(config_path, 'strategy_conf.json')
    if os.path.isdir(params_path):
        params_path = os.path.join(params_path, 'strategy_conf.json')

    with open(params_path, 'w', encoding='utf8') as f:
        json.dump(params, f, ensure_ascii=False)


def load_all_strategy_config(conf_file=''):
    if conf_file:
        conf0 = load_strategy_conf(params_path=conf_file)
    else:
        with open(strategy_config_yaml_file, 'r', encoding='utf8') as f:
            conf0 = yaml.safe_load(f)

    all_setting = {}
    stra_instances = {}
    conf = conf0['stra_conf']
    if not conf:
        return all_setting, stra_instances

    for sk, sv in conf.items():  # 所有策略类配置
        if isinstance(sv, dict):
            setting = StraSetting()
            ds = setting.__dict__
            ds['name'] = sk
            for k, v in sv.items():
                if k in ds.keys():
                    if k == 'params':  # v={pname:pvalue},  type(pvalue)==StrategyParam
                        params = {}
                        for pk, pv in v.items():
                            param = StraParam()
                            dp = param.__dict__
                            for pvk, pvv in pv.items():
                                dp[pvk] = pvv
                            params[pk] = dp
                        ds[k] = params
                    else:
                        ds[k] = v
            all_setting[sk] = ds
            if 'instances' in sv.keys():
                stra_instance = sv['instances']
                for k, v in stra_instance.items():
                    dsi = ds.copy()
                    dsi['name'] = k
                    if k in dsi.keys():
                        if k == 'params':
                            params = {}
                            for pk, pv in v.items():
                                param = StraParam()
                                dp = param.__dict__
                                dp['name'] = pk
                                for pvk, pvv in pv.items():
                                    dp[pvk] = pvv
                                params[pk] = dp
                            dsi[k] = params
                        else:
                            dsi[k] = v
                    dsi['symbol'] = v['symbol']
                    dsi['period'] = v['period']
                    dsi['stra_params'] = dsi['params']
                    dsi['params'] = v['params']
                    dsi['id'] = '_'.join([sk,k])
                    del dsi['instances']
                    stra_instances['_'.join([sk, k])] = dsi
                    # all_setting['_'.join([sk, k])] = dsi

    return all_setting, stra_instances


all_strategy_config, all_stra_instance = load_all_strategy_config()


def get_strategy_config(strategy_class_name='', symbol='', period=0, instance_name='', save_new=True):
    """
    获取指定策略类、标的及周期的实例配置信息，
    strategy_class_name symbol period 要同时提供
    如果没有该实例的配置信息，调入类的配置并利用给定的标的、周期生成新的实例配置信息，并回写
    :param strategy_class_name:
    :param symbol: 标的代码
    :param period: 周期
    :param instance_name: 策略实例名称，格式：strategyclassname_symbol_period ，有且仅有2个"_"
    :param save_new: 是否保存新的实例配置信息
    :return: StrategySetting.__dict__
    """
    all_conf = all_strategy_config
    if instance_name:
        if instance_name in all_conf.keys():
            return all_conf[instance_name]
        else:
            class_name, symbol, period = instance_name.split('_')
            pid = ''.join([ltr for ltr in symbol if not ltr.isnumeric()])
            sin = '_'.join([class_name, pid, str(period)])
            if class_name in all_conf.keys():
                new_conf = all_conf[class_name].copy()
                new_conf['symbol'] = symbol
                new_conf['period'] = period
                new_conf['name'] = instance_name
                all_conf[sin] = new_conf
                if save_new:
                    with open(strategy_config_json_file, 'w', encoding='utf-8') as f:
                        json.dump(all_conf, f, ensure_ascii=False)
                return new_conf
            return {}

    product_class = ''.join([ltr for ltr in symbol if not ltr.isnumeric()])
    sin = '_'.join([strategy_class_name, product_class, str(period)])  # 策略实例名
    if sin in all_conf.keys():
        new_conf = all_conf[sin].copy()
        new_conf['symbol'] = symbol
        new_conf['period'] = period
        return new_conf
    if strategy_class_name in all_conf.keys():
        new_conf = all_conf[strategy_class_name].copy()
        new_conf['symbol'] = symbol
        new_conf['period'] = period
        new_conf['name'] = sin
        all_conf[sin] = new_conf
        if save_new:
            with open(strategy_config_json_file, 'w', encoding='utf8') as f:
                json.dump(all_conf, f, ensure_ascii=False)
        return new_conf


def create_strategy_instance(strategy_class_name, symbol, period, strategy_engine, strategy_conf=None):
    """
    创建策略实例
    :param strategy_class_name: 策略类名称，区分大小写
    :param symbol: 实例作用的标的代码
    :param period: 周期
    :param strategy_engine: 策略引擎
    :param strategy_conf: 实例用的配置信息
    :return: 策略实例
    """
    if not strategy_conf:
        strategy_conf = get_strategy_config(strategy_class_name, symbol, period)
    strategy_module = import_strategy_module(strategy_conf['pkg'])
    strategy_module = importlib.reload(strategy_module)
    strategy_cls = getattr(strategy_module, strategy_class_name)
    # si = create_class_instance(strategy_conf['pkg'], strategy_class_name, strategy_engine, strategy_conf)
    si = strategy_cls(strategy_engine, strategy_conf)
    return si


def get_strategy_result_items():
    conf_file = 'strategy_result_items.yml'
    conf_file = os.path.join(yml_file_path, conf_file)
    conf = {}
    with open(conf_file, encoding='utf8') as f:
        conf = yaml.load(f)

    return conf


def get_strategy_result_items_list():
    conf_file = 'strategy_result_items.yml'
    conf_file = os.path.join(yml_file_path, conf_file)
    conf = {}
    with open(conf_file, encoding='utf8') as f:
        conf = yaml.load(f)
    rstl = get_no_child(conf)
    return rstl


def get_result_display_items_dict():
    conf_file = 'bt_rslt_dp_itm.yml'
    conf_file = os.path.join(yml_file_path, conf_file)
    conf = {}
    with open(conf_file, encoding='utf8') as f:
        conf = yaml.safe_load(f)
    rstl = conf
    return rstl


def get_strategy_setting(conf_file='stra_cfg.yml'):
    """
    从配置文件 stra_cfg.yml 中读出策略的分类信息及策略实例的配置信息
    :param conf_file:
    :return: strategy_types, strategy_detail
    """
    conf_file = os.path.join(yml_file_path, conf_file)
    conf = {}
    with open(conf_file, encoding='utf8') as f:
        conf = yaml.safe_load(f)
        strategy_types = conf['分类']
        strategy_detail = conf['stra_conf']
    return strategy_types, strategy_detail


all_strategy_classifications, strategy_conf_details = get_strategy_setting()


def import_strategy_module(strategy_module_name):
    """
    载入策略模块
    :param strategy_module_name: .py file name
    :return:
    """
    pkg = strategy_module_name
    if pkg.find('.') == -1:
        pkg = '.'.join([strategy_path, strategy_module_name])
    strategy_module = importlib.import_module(pkg)
    return strategy_module


def get_strategy_pkg(strategy_class_name):
    """
    获取策略所在的包
    :param strategy_class_name:
    :return:
    """
    if strategy_class_name in strategy_conf_details.keys():
        pkg = strategy_conf_details[strategy_class_name]['pkg']
        return pkg
    else:
        return None


def get_strategy_class(strategy_class_name):
    """
    载入策略类
    :param strategy_class_name: 策略类名称，区分大小写
    :return: 策略实例
    """
    pkg = get_strategy_pkg(strategy_class_name)
    strategy_module = import_strategy_module(pkg)
    strategy_module = importlib.reload(strategy_module)  # 重新加载策略，保证运行时修改的策略即时生效
    strategy_cls = getattr(strategy_module, strategy_class_name)
    # print(pkg,strategy_class_name,strategy_cls.__dict__)
    return strategy_cls


if __name__ == '__main__':
    # ncf = os.path.join(config_path, 'strategy_conf.json')
    # print(ncf)
    # sc = all_strategy_config
    # cn = 'TrendStrategy'
    # print(sc.keys())
    # print(sc['TrendStrategy_ni_0'])
    # symbol = 'ag1812'
    # period = 0
    # pc = ''.join([ltr for ltr in symbol if not ltr.isnumeric()])
    # print(pc)
    # sin = '_'.join([cn, pc, str(period)])
    # print(sin)
    # st = get_strategy_config(instance_name=sin)
    # st = load_all_strategy_config()  # get_result_display_items_dict()
    st=all_stra_instance
    print(type(st))
    for k, v in st.items():
        print(k, ':', v)
    # print(all_strategy_classifications)
    # print(strategy_conf_details)
